package com.info.zhiduoduo.core.service.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.mapper.Condition;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.info.zhiduoduo.common.annotation.DependOnEntity;
import com.info.zhiduoduo.common.annotation.LogEntity;
import com.info.zhiduoduo.common.annotation.LogParameter;
import com.info.zhiduoduo.common.annotation.ZddLog;
import com.info.zhiduoduo.common.constants.system.UserType;
import com.info.zhiduoduo.common.constants.system.ZddAppEnum;
import com.info.zhiduoduo.common.constants.system.ZddLogType;
import com.info.zhiduoduo.common.exception.DependOnEntityNotDefinedException;
import com.info.zhiduoduo.common.exception.ZddLogCycleDependOnException;
import com.info.zhiduoduo.common.exception.ZddLogEntityParamenterEmptyException;
import com.info.zhiduoduo.common.exception.ZddLogFieldNotFoundException;
import com.info.zhiduoduo.common.exception.ZddLogFieldParameterSizeException;
import com.info.zhiduoduo.common.exception.ZddLogParamenterNotFoundException;
import com.info.zhiduoduo.common.formbean.BaseAuthInfo;
import com.info.zhiduoduo.common.formbean.BaseSimpleFormBean;
import com.info.zhiduoduo.common.utils.ContextUtil;
import com.info.zhiduoduo.common.utils.DateUtil;
import com.info.zhiduoduo.common.utils.NumberUtil;
import com.info.zhiduoduo.common.utils.ReflectionUtil;
import com.info.zhiduoduo.common.utils.StringUtil;
import com.info.zhiduoduo.core.dto.operationlog.OperationLogContext;
import com.info.zhiduoduo.core.dto.operationlog.OperatorInfo;
import com.info.zhiduoduo.repository.dto.company.CompanyDetailDto;
import com.info.zhiduoduo.repository.dto.crowdsource.CrowdsourceInfoDto;
import com.info.zhiduoduo.repository.dto.maker.MakerDetailDto;
import com.info.zhiduoduo.repository.dto.partner.PartnerDetailDto;
import com.info.zhiduoduo.repository.dto.system.QueryManagerDto;
import com.info.zhiduoduo.repository.entity.system.SystemOperationLog;
import com.info.zhiduoduo.repository.mapper.company.CompanyMapper;
import com.info.zhiduoduo.repository.mapper.crowdsource.CrowdsourceAccountMapper;
import com.info.zhiduoduo.repository.mapper.maker.MakerMapper;
import com.info.zhiduoduo.repository.mapper.partner.PartnerMapper;
import com.info.zhiduoduo.repository.mapper.system.ManagerMapper;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RestController;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Resource;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class OperationLogHelper implements ApplicationRunner {

	@Resource
	MakerMapper makerMapper;
	@Resource
	CompanyMapper companyMapper;
	@Resource
	PartnerMapper partnerMapper;
	@Resource
	CrowdsourceAccountMapper crowdsourceAccountMapper;
	@Resource
	ManagerMapper managerMapper;

	@Override
	public void run(ApplicationArguments args) throws Exception {
		// 启动项目时，遍历所有的controller，找到有@ZddLog的方法，检测注解是否有效
		Map<String, Object> beanMap = ContextUtil.getContext().getBeansWithAnnotation(RestController.class);
		beanMap.forEach((k, c) -> ReflectionUtil.findAllMethodsWithAnnotation(c.getClass(), ZddLog.class).forEach(m -> {
			ZddLog zddLog = m.getAnnotation(ZddLog.class);
			LogEntity[] entityAnnos = zddLog.entities();

			// 检查依赖是否正确，否则抛异常
			checkDependOnValid(entityAnnos, m.getName());

			// 检查参数设置是否正确，比如定义的字段是否存在等等，否则抛异常。
			for (LogEntity entityAnno : entityAnnos) {
				checkParameterValid(m, entityAnno);
			}
		}));
	}

	/**
	 * 调用业务接口之前初始化数据，查询要记录日志的entities，保存相关的上下文
	 *
	 * @return 操作日志上下文对象
	 */
	public OperationLogContext doBeforeOperate(ZddLog zddLog, ZddAppEnum zddAppEnum, ProceedingJoinPoint jp, String url, String urlParams)
			throws Exception {
		if (zddLog == null) return null;

		OperationLogContext context = OperationLogContext.of(zddLog, zddAppEnum, jp, url, urlParams);

		// 获取当前的操作人信息
		OperatorInfo operateInfo = retrieveOperator(context);
		context.setOperatorInfo(operateInfo);

		if (ArrayUtils.isEmpty(context.getMethodSignature().getParameterNames())) {
			log.info(String.format("接口方法【%s】没有定义参数，忽略记录操作日志。", context.getMethod().getName()));
			return null;
		}

		LogEntity[] entityAnnos = zddLog.entities();

		// 获取所有Entity初始化值，即根据接口请求参数和配置的字段通过Mybatis-plus加载出所有需要的Entity
		if (ArrayUtils.isNotEmpty(entityAnnos)) {
			// 第一次获取数据，其中可能有些要依赖其他entity，先忽略
			for (LogEntity entityAnno : entityAnnos) {
				retrieveEntity(entityAnno, context);
			}

			// 处理依赖的数据
			processDependOnEntities(context);
		}

		return context;
	}

	public OperationLogContext doPostOperate(Object obj, OperationLogContext context)
			throws InstantiationException, IllegalAccessException {
		// 根据所有的原始entity 获取更新后的entity
		Map<Class<? extends Model<?>>, List<Model<?>>> initialEntities = context.getInitialEntities();
		Map<Class<? extends Model<?>>, List<Model<?>>> afterEntities = Maps.newHashMap();

		// 日志全文builder
		StringBuilder sb = new StringBuilder();

		// 如果是CREATE 日志，则直接显示请求参数中的数据就行
		ZddLog zddLog = context.getZddLog();
		if (zddLog.type() == ZddLogType.CREATE || zddLog.type() == ZddLogType.QUERY) {
			sb.append(assembleLogCreateContent(context));
		} else {
			// UPDATE 和 DELETE 操作都会有
			for (Map.Entry<Class<? extends Model<?>>, List<Model<?>>> entry : initialEntities.entrySet()) {
				Class<? extends Model<?>> clazz = entry.getKey();
				List<Model<?>> list = entry.getValue();

				Model<?> instance = clazz.newInstance();
				List<Model<?>> newList = Lists.newArrayList();
				for (Model<?> entity : list) {
					// 获取主键的值
					Method pkValMethod = ReflectionUtils.findMethod(entity.getClass(), "pkVal");
					if (pkValMethod == null) {
						log.info("doPostOperate, 在【{}】Model类中没有找到pkVal()方法", entity.getClass().getName());
						continue;
					}

					pkValMethod.setAccessible(true);
					Model newEntity = instance.selectById((Serializable) ReflectionUtils.invokeMethod(pkValMethod, entity));
					newList.add(newEntity);

					// 组装操作日志内容
					sb.append(assembleLogContent(context, entity, newEntity)).append("\n");
				}
				afterEntities.put(clazz, newList);
			}
		}

		// 保存到数据库
		OperatorInfo operatorInfo = context.getOperatorInfo();
		SystemOperationLog log = new SystemOperationLog().setOperatorid(operatorInfo.getUserid())
				.setUsername(operatorInfo.getName())
				.setUserType(operatorInfo.getUserType())
				.setName(zddLog.name())
				.setType(zddLog.type())
				.setZddApp(context.getZddAppEnum().id)
				.setMethod(context.getMethod().getName())
				.setUrl(context.getUrl())
				.setUrlParams(context.getUrlParams())
				.setRequestBody(JSON.toJSONString(context.getRequestBody()))
				.setContent(sb.toString())
				.setLogTime(new Date())
				.setReturnValue(JSON.toJSONString(obj));
		log.insert();

		return context;
	}

	private String assembleLogContent(OperationLogContext context, Model<?> oldEntity, Model<?> newEntity) {
		// ZddLogType#CREATE UPDATE DELETE QUERY 等等
		int type = context.getZddLog().type();
		String content = "";
		if (type == ZddLogType.CREATE) {
			content = assembleLogCreateContent(context);
		} else if (type == ZddLogType.UPDATE) {
			content = assembleLogUpdateContent(context, oldEntity, newEntity);
		} else if (type == ZddLogType.DELETE) {
			content = assembleLogDeleteContent(context, oldEntity, newEntity);
		} else if (type == ZddLogType.QUERY) {
			// content = assembleLogQueryContent(context, oldEntity, newEntity);
		}
		return content;
	}

	private String assembleLogCreateContent(OperationLogContext context) {
		Object requestBody = context.getRequestBody();
		if (requestBody instanceof Collection) {
			StringBuilder sb = new StringBuilder();
			for (Object obj : (Collection) requestBody) {
				sb.append(assembleLogCreateContent(obj)).append("\n");
			}
			return sb.toString();
		} else {
			return assembleLogCreateContent(requestBody);
		}
	}

	private String assembleLogCreateContent(Object obj) {
		if (obj == null) return "";

		// 如果是BaseSimpleFormBean
		if (obj instanceof BaseSimpleFormBean) {
			obj = ((BaseSimpleFormBean) obj).getReqdata();
		}

		StringBuilder sb = new StringBuilder().append("新建：");
		if (ReflectionUtil.isPrimitiveOrString(obj.getClass())) {
			sb.append(obj);
		} else {
			JSONObject jo = (JSONObject) JSON.toJSON(obj);
			jo.forEach((k, v) -> {
				String s = parseStr(v);
				if (s == null) return;
				sb.append(k).append("=").append(s).append("，");
			});
		}
		return prune2Period(sb.toString());
	}

	private String parseStr(Object v) {
		if (v == null) return null;
		String s = "";
		if (v instanceof Date) {
			s = DateUtil.getStringDate((Date) v, DateUtil.FORMAT_STYLE_1);
		} else {
			s = String.valueOf(v);
		}
		return s;
	}

	private String assembleLogUpdateContent(OperationLogContext context, Model<?> oldEntity, Model<?> newEntity) {
		if (oldEntity == null) {
			log.info("The oldEntity is null. Cann't assemble the log content.");
			return "";
		} else if (newEntity == null) {
			log.info("The newEntity is null. Cann't assemble the log content.");
			return "";
		}

		StringBuilder sb = new StringBuilder();
		JSONObject oldJo = (JSONObject) JSON.toJSON(oldEntity);
		JSONObject newJo = (JSONObject) JSON.toJSON(newEntity);
		oldJo.forEach((k, v) -> {
			String oldV = parseStr(v);
			String newV = parseStr(newJo.get(k));
			if (!StringUtils.equals(oldV, newV)) {
				sb.append("[").append(k).append("]").append("由 ").append(oldV).append(" 修改为 ").append(newV).append("，");
			}
		});

		return prune2Period(sb.toString());
	}

	private String prune2Period(String content) {
		if (StringUtil.isEmpty(content)) return content;
		if (content.endsWith("，")) {
			content = content.substring(0, content.length() - 1) + "。";
		}
		return content;
	}

	private String assembleLogDeleteContent(OperationLogContext context, Model<?> oldEntity, Model<?> newEntity) {
		if (oldEntity == null) return "";

		JSONObject jo = (JSONObject) JSON.toJSON(oldEntity);
		StringBuilder sb = new StringBuilder().append("删除：");
		jo.forEach((k, v) -> {
			String s = parseStr(v);
			if (s == null) return;
			sb.append(k).append("=").append(s).append("， ");
		});
		return prune2Period(sb.toString());
	}

	private String assembleLogQueryContent(OperationLogContext context, Model<?> oldEntity, Model<?> newEntity) {
		return "";
	}

	private OperatorInfo retrieveOperator(OperationLogContext context) {
		OperatorInfo operator = OperatorInfo.of();
		operator.setZddAppEnum(context.getZddAppEnum());

		// 主要从RequestBody中获取操作人。如果是URL参数则目前获取不到
		Object requestBody = context.getRequestBody();
		if (requestBody == null) {
			log.info("记录操作日志，没有RequestBody请求参数，获取操作人失败！！！！");
			return operator;
		}
		if (!(requestBody instanceof BaseAuthInfo)) {
			log.info("requestBody 不是BaseAuthInfo 类型，无法获取操作人id和type！！！");
			return operator;
		}

		BaseAuthInfo authInfo = (BaseAuthInfo) requestBody;
		int userid = authInfo.getUserid();
		int idtype = authInfo.getIdtype();
		operator.setUserid(userid);
		operator.setUserType(idtype);
		// 根据userType获取对应的真实姓名

		String name = "";
		if (NumberUtil.intIn(idtype, UserType.MAKER, UserType.TAXER, UserType.MAKER_CROWDSOURCE)) {
			MakerDetailDto userDetail = makerMapper.getUserDetail(userid);
			if (userDetail != null) name = userDetail.getRealname();
		} else if (idtype == UserType.COMPANY) {
			CompanyDetailDto companyInfo = companyMapper.getCompanyInfo(userid);
			if (companyInfo != null) name = companyInfo.getCompname();
		} else if (idtype == UserType.PARTNER) {
			PartnerDetailDto partnerDetail = partnerMapper.getPartnerDetail(userid);
			if (partnerDetail != null) name = partnerDetail.getRealname();
		} else if (idtype == UserType.CROWDSOURCE) {
			CrowdsourceInfoDto crowdsourceInfoDto = crowdsourceAccountMapper.getInfo(userid);
			if (crowdsourceInfoDto != null) name = crowdsourceInfoDto.getRealname();
		} else if (operator.getZddAppEnum() == ZddAppEnum.ZDD_MANAGEMENT) {
			// 如果管理后台的请求，则是系统管理员账号
			QueryManagerDto manager = managerMapper.getManagerById(userid);
			if (manager != null) name = manager.getManagername();
		}
		operator.setName(name);
		return operator;
	}

	private void processDependOnEntities(OperationLogContext context) throws InstantiationException, IllegalAccessException {
		Map<Class<? extends Model<?>>, LogEntity> pendingDependOnEntities = context.getPendingDependOnEntities();
		if (!CollectionUtils.isEmpty(pendingDependOnEntities)) {
			// 2层while循环，直到所有的依赖entity数据加载成功。
			do {
				Iterator<Map.Entry<Class<? extends Model<?>>, LogEntity>> it = pendingDependOnEntities.entrySet().iterator();
				while (it.hasNext()) {
					Map.Entry<Class<? extends Model<?>>, LogEntity> next = it.next();
					retrieveEntity(next.getValue(), context);
					// 如果已经加载数据成功，删除这个
					if (context.getInitialEntities().containsKey(next.getKey())) {
						it.remove();
					}
				}
			} while (!CollectionUtils.isEmpty(pendingDependOnEntities));
		}
	}

	private void checkDependOnValid(LogEntity[] entityAnnos, String methodName) {
		if (ArrayUtils.isNotEmpty(entityAnnos)) {
			Map<Class<? extends Model<?>>, List<Class<? extends Model<?>>>> allDependOnEntityMap = Maps.newHashMap();

			List<? extends Class<? extends Model<?>>> entityClasses = Stream.of(entityAnnos)
					.map(LogEntity::entity)
					.collect(Collectors.toList());
			// 整理依赖链
			for (LogEntity anno : entityAnnos) {
				Class<? extends Model<?>> entityClass = anno.entity();
				DependOnEntity[] dependOnEntityAnnos = anno.dependOnEntities();
				if (ArrayUtils.isNotEmpty(dependOnEntityAnnos)) {

					List<Class<? extends Model<?>>> dependOnList = Lists.newLinkedList();
					for (DependOnEntity dependOnEntityAnno : dependOnEntityAnnos) {
						// 如果被依赖的entity没在定义的几个entities中，报错
						Class<? extends Model<?>> denpendOnClass = dependOnEntityAnno.entity();
						if (!entityClasses.contains(denpendOnClass)) {
							throw new DependOnEntityNotDefinedException(
									String.format("接口方法【%s】中，被依赖的【%s】Entity Class没有在接口的@ZddLog注解的entities()方法中定义", methodName,
											denpendOnClass.getName()));
						}
						dependOnList.add(denpendOnClass);
					}
					allDependOnEntityMap.put(entityClass, dependOnList);
				}
			}

			for (Class<? extends Model<?>> entityClazz : entityClasses) {
				// 检查是否有Entity的循环依赖，按照一个Class的依赖链遍历下去，如果最终发现了自己，那就说明存在循环依赖（形成了一个环），直接抛出异常
				if (findMyselfInDependOnChain(entityClazz, entityClazz, allDependOnEntityMap)) {
					throw new ZddLogCycleDependOnException(
							String.format("接口方法【%s】中，【%s】Entity Class发生了循环依赖", methodName, entityClazz.getName()));
				}
			}
		}
	}

	private boolean findMyselfInDependOnChain(Class<? extends Model<?>> myselfclazz, Class<? extends Model<?>> dependOnclazz,
	                                          Map<Class<? extends Model<?>>, List<Class<? extends Model<?>>>> allDependOnEntityMap) {
		List<Class<? extends Model<?>>> dependOnclasses = allDependOnEntityMap.get(dependOnclazz);
		if (CollectionUtils.isEmpty(dependOnclasses)) {
			return false;
		}

		// 查看当前被依赖的类的的依赖类中是否有自己
		if (dependOnclasses.contains(myselfclazz)) return true;

		// 没有自己，继续下一层依赖
		boolean findMyself;
		for (Class<? extends Model<?>> nextLevelDependOnClazz : dependOnclasses) {
			findMyself = findMyselfInDependOnChain(myselfclazz, nextLevelDependOnClazz, allDependOnEntityMap);
			// 在下一层找到了自己，不需要继续遍历，直接返回true，表示存在循环依赖
			if (findMyself) return true;
		}

		// 所有下一层依赖都没有找到自己，则依赖是安全的，返回false。
		return false;
	}

	private void checkParameterValid(Method method, LogEntity entityAnno) {

		Class<? extends Model<?>> logEntityClass = entityAnno.entity();
		DependOnEntity[] dependOnEntityAnnos = entityAnno.dependOnEntities();
		LogParameter[] parameterAnnos = entityAnno.parameters();
		String methodName = method.getName();

		// dependOn依赖对象的参数是否有效
		if (ArrayUtils.isNotEmpty(dependOnEntityAnnos)) {
			for (DependOnEntity dependOnAnno : dependOnEntityAnnos) {
				Class<? extends Model<?>> dependOnClass = dependOnAnno.entity();

				// SelectOne的查询条件的field
				// dependOnFields与dependOnParameters的数据必须要一致
				String[] dependOnFields = dependOnAnno.fields();
				String[] dependOnParameters = dependOnAnno.parameters();
				if (!ArrayUtils.isSameLength(dependOnFields, dependOnParameters)) {
					throw new ZddLogFieldParameterSizeException(String.format(
							"接口方法【%s】的@ZddLogEntity【%s】里面的@DependOnEntity【%s】定义的【fields().length = %d】 与【parameters().length = %d】不一致。",
							methodName, logEntityClass.getSimpleName(), dependOnClass.getSimpleName(), dependOnFields.length,
							dependOnParameters.length));
				}
				// 检查依赖类中是否存在定义的字段
				for (int i = 0; i < dependOnFields.length; i++) {
					String field = dependOnFields[i];
					String parameter = dependOnParameters[i];
					// entity中是否有指定的field
					if (!isFieldOrColumn(logEntityClass, field)) {
						throw new ZddLogFieldNotFoundException(
								String.format("接口方法【%s】的@ZddLogEntity【%s】里面没有找到@DependOnEntity【%s】需要的字段【%s】", methodName,
										logEntityClass.getSimpleName(), dependOnClass.getName(), field));
					}
					// 依赖的Class中没有这个指定的parameter
					if (!ReflectionUtil.hasField(dependOnClass, parameter)) {
						throw new ZddLogFieldNotFoundException(
								String.format("接口方法【%s】的@ZddLogEntity【%s】里面的@DependOnEntity【%s】类中没有找到字段【%s】", methodName,
										logEntityClass.getSimpleName(), dependOnClass.getName(), parameter));
					}
				}
			}
		}

		// 检查请求参数是否有效
		if (ArrayUtils.isNotEmpty(parameterAnnos)) {
			for (LogParameter parameterAnno : parameterAnnos) {
				// SelectOne的查询条件的field
				// dependOnFields与dependOnParameters的数据必须要一致
				String[] fieldNames = parameterAnno.fields();
				String[] parameterNames = parameterAnno.parameters();
				if (!ArrayUtils.isSameLength(fieldNames, parameterNames)) {
					throw new ZddLogFieldParameterSizeException(
							String.format("接口方法【%s】的@ZddLogParameter注解上定义的【fields().length = %d】 与【parameters().length = %d】不一致。",
									methodName, fieldNames.length, parameterNames.length));
				}
				if (ArrayUtils.isEmpty(parameterNames)) {
					return;
				}
				// 判断请求参数中是否存在需要的字段
				Parameter[] parametersOnMethod = method.getParameters();
				List<String> parameterNamesOnMethod = Arrays.stream(parametersOnMethod)
						.map(Parameter::getName)
						.collect(Collectors.toList());
				log.debug("接口方法【{}】形式参数列表【{}】，要查找的请求参数【{}】", methodName, JSON.toJSONString(parameterNamesOnMethod),
						JSON.toJSONString(parameterNames));

				if (CollectionUtils.isEmpty(parameterNamesOnMethod) && !ArrayUtils.isEmpty(parameterNames)) {
					throw new ZddLogFieldParameterSizeException(
							String.format("接口方法【%s】的@ZddLogEntity【%s】里面的@ZddLogParameter定义的【parameters().length = %d】，但该方法没有定义形式参数。",
									methodName, logEntityClass.getSimpleName(), parameterNames.length));
				}
				// 检查每个定义的参数
				// checkParameterFieldMatch(methodName, parametersOnMethod, fieldNames, parameterNames);
			}
		}
	}

	private void retrieveEntity(LogEntity entityAnno, OperationLogContext context) throws InstantiationException, IllegalAccessException {
		Class<? extends Model<?>> entityClass = entityAnno.entity();
		DependOnEntity[] dependOnEntityAnnos = entityAnno.dependOnEntities();
		LogParameter[] parameterAnnos = entityAnno.parameters();
		String methodName = context.getMethod().getName();

		// 每个field对应的值，可能会对应一个列表，表示要查询多个
		Map<String, List<Object>> fieldValuesMap = Maps.newHashMap();

		// dependOn依赖对象的参数
		if (ArrayUtils.isNotEmpty(dependOnEntityAnnos)) {
			for (DependOnEntity dependOnAnno : dependOnEntityAnnos) {
				Class<? extends Model<?>> dependOnClass = dependOnAnno.entity();
				List<Model<?>> modelList = context.getInitialEntities().get(dependOnClass);
				// 查看依赖的ZddLogEntity 是否已经加载好了，没有的话直接返回，等加载完了再来加载这个
				if (modelList == null) {
					context.getPendingDependOnEntities().put(dependOnClass, entityAnno);
					return;
				} else {
					// SelectOne的查询条件的field
					// dependOnFields与dependOnParameters的数据必须要一致
					String[] dependOnFields = dependOnAnno.fields();
					String[] dependOnParameters = dependOnAnno.parameters();
					if (!ArrayUtils.isSameLength(dependOnFields, dependOnParameters)) {
						throw new ZddLogFieldParameterSizeException(
								String.format("接口方法【%s】的@DependOnEntity注解上定义的【fields().length = %d】 与【parameters().length = %d】不一致。",
										methodName, dependOnFields.length, dependOnParameters.length));
					}
					// 从依赖对象中获取查询Entity的参数
					for (Model<?> dependOnEntity : modelList) {
						retrieveFieldsValues(fieldValuesMap, dependOnEntity, dependOnFields, dependOnParameters);
					}
				}
			}
		}

		// 请求参数中的参数
		if (ArrayUtils.isNotEmpty(parameterAnnos)) {
			for (LogParameter parameterAnno : parameterAnnos) {
				// SelectOne的查询条件的field
				// dfieldNames与parameterNames的数据必须要一致
				String[] fieldNames = parameterAnno.fields();
				String[] parameterNames = parameterAnno.parameters();
				if (!ArrayUtils.isSameLength(fieldNames, parameterNames)) {
					throw new ZddLogFieldParameterSizeException(
							String.format("接口方法【%s】的@ZddLogParameter注解上定义的【fields().length = %d】 与【parameters().length = %d】不一致。",
									methodName, fieldNames.length, parameterNames.length));
				}
				// 从接口的请求参数中获取查询Entity的参数
				retrieveFieldsValues(fieldValuesMap, context, fieldNames, parameterNames);
			}
		}

		// 组装查询参数，笛卡尔积的方式组装
		List<Map<String, Object>> selectParameters;
		List<List<Map<String, Object>>> lists = Lists.newArrayList();
		fieldValuesMap.forEach((k, v) -> {
			List<Map<String, Object>> list = v.stream().map(o -> {
				HashMap<String, Object> m = Maps.newHashMap();
				m.put(k, o);
				return m;
			}).collect(Collectors.toList());
			lists.add(list);
		});
		selectParameters = lists.get(0);
		if (lists.size() > 1) {
			for (int i = 1; i < lists.size(); i++) {
				List<Map<String, Object>> currList = lists.get(i);
				selectParameters = selectParameters.stream().flatMap(m1 -> currList.stream().map(m2 -> {
					m1.putAll(m2);
					return m1;
				})).collect(Collectors.toList());
			}
		}



		/*List<Stream<Map<String, Object>>> list = Lists.newArrayList();
		fieldValuesMap.forEach((k, v) -> {
			Stream<Map<String, Object>> stream = v.stream().map(o -> {
				HashMap<String, Object> m = Maps.newHashMap();
				m.put(k, o);
				return m;
			});
			list.add(stream);
		});
		selectParameters = cartesian((m1, m2) -> {
			m1.putAll(m2);
			return m1;
		}, list);*/

		// 定义Mybatis-plus的SelectOne的查询条件
		// List<Map<String, Object>> selectParameters = Lists.newArrayList();

		// 通过所有参数查询Entity数据
		if (CollectionUtils.isEmpty(selectParameters)) {
			log.info("selectParameters 参数为空");
			throw new ZddLogEntityParamenterEmptyException(
					String.format("接口方法【%s】 获取@ZddLogEntity 【%s】类的对象时没有找到对应的参数值", methodName, entityClass.getName()));
		}

		List<Model<?>> entityList = Lists.newArrayList();
		for (Map<String, Object> map : selectParameters) {
			Condition condition = Condition.create();
			for (Map.Entry<String, Object> entry : map.entrySet()) {
				condition.eq(entry.getKey(), entry.getValue());
			}
			// 新建entity对象
			if (entityAnno.isList()) {
				List<Model<?>> list = (List<Model<?>>) entityClass.newInstance().selectList(condition);
				if (!CollectionUtils.isEmpty(list)) {
					entityList.addAll(list);
				} else {
					log.info("接口方法【{}】，加载【{}】Entity对象list失败，参数：【{}】", methodName, entityClass.getName(),
							JSON.toJSONString(selectParameters));
				}
			} else {
				Model<?> entity = entityClass.newInstance().selectOne(condition);
				if (entity != null) {
					entityList.add(entity);
				} else {
					log.info("接口方法【{}】，加载【{}】Entity对象失败，参数：【{}】", methodName, entityClass.getName(), JSON.toJSONString(selectParameters));
				}
			}
		}

		// 获取到了entity，存入map
		context.getInitialEntities().put(entityClass, entityList);
		context.getInitialParameters().put(entityClass, selectParameters);
	}

	/**
	 * 将几个流采用笛卡尔积的形式合并
	 *
	 * @param aggregator
	 * @param list
	 * @return
	 */
	private static List<Map<String, Object>> cartesian(BinaryOperator<Map<String, Object>> aggregator,
	                                                   List<Stream<Map<String, Object>>> list) {
		return list.stream()
				.reduce((s1, s2) -> s1.flatMap(t1 -> s2.map(t2 -> aggregator.apply(t1, t2))))
				.orElse(Stream.empty())
				.collect(Collectors.toList());
	}

	private void retrieveFieldsValues(Map<String, List<Object>> fieldValuesMap, Model<?> dependOnEntity, String[] fields,
	                                  String[] parameters) {
		Map<String, Object> parameterMap = Maps.newHashMap();
		for (int i = 0; i < fields.length; i++) {
			String field = fields[i];
			String parameter = parameters[i];
			// 依赖的Class中没有这个指定的parameter
			if (!ReflectionUtil.hasField(dependOnEntity.getClass(), parameter)) {
				throw new ZddLogFieldNotFoundException(
						String.format("在DepenOnEntity【%s】类中没有找到字段【%s】", dependOnEntity.getClass().getName(), parameter));
			}
			// Retrieve value of this parameter from the dependOnEntity object
			Object value = ReflectionUtil.getValue(dependOnEntity, parameter);
			fieldValuesMap.put(field, Lists.newArrayList(value));
		}
	}

	/**
	 * 判断这个model对象类型是否包含指定的field字段或为表column
	 *
	 * @param clazz Mybatis-plus Model对象类型
	 * @param field 字段名或column名
	 * @return
	 */
	private boolean isFieldOrColumn(Class<? extends Model<?>> clazz, String field) {
		// 判断这个字段是否是表column注解标记的 字段名。比如：
		//     @TableField("project_no")
		//     private String projectNo;
		boolean isColumn = Arrays.stream(clazz.getFields()).anyMatch(f -> {
			f.setAccessible(true);
			if (f.isAnnotationPresent(TableField.class)) {
				return StringUtil.equals(f.getAnnotation(TableField.class).value(), field);
			} else if (f.isAnnotationPresent(TableId.class)) {
				return StringUtil.equals(f.getAnnotation(TableId.class).value(), field);
			}
			return false;
		});
		// 判断是否是Entity字段或者是表column
		return ReflectionUtil.hasField(clazz, field) || isColumn;
	}

	private void retrieveFieldsValues(Map<String, List<Object>> fieldValuesMap, OperationLogContext context, String[] fieldNames,
	                                  String[] parameterNames) {
		// 接口方法的形式参数名称列表
		String[] parameterNamesOnMethod = context.getMethodSignature().getParameterNames();
		String methodName = context.getMethod().getName();
		log.info("接口方法【{}】形式参数列表【{}】，要查找的请求参数【{}】", methodName, JSON.toJSONString(parameterNamesOnMethod),
				JSON.toJSONString(parameterNames));

		if (ArrayUtils.isEmpty(parameterNamesOnMethod)) {
			log.info("接口方法【{}】没有形式参数列表，无法查找对应的请求参数【{}】", methodName, JSON.toJSONString(parameterNames));
			return;
		}

		for (int i = 0; i < fieldNames.length; i++) {
			String fieldName = fieldNames[i];
			String parameterName = parameterNames[i];
			List<Object> valueList = Lists.newArrayList();
			List<String> parameterNameParts = StringUtil.splitToStr(parameterName, ".");

			// 第一层，先在方法的形式参数列表上查找
			Object arg = null;
			String part = parameterNameParts.get(0);

			// 为了防止在方法的形参列表就遇到逗号分隔的字符串形式，先prune掉[]格式
			int index = Lists.newArrayList(parameterNamesOnMethod).indexOf(pruneMultiPart(part));
			if (index >= 0) {
				// 找到了
				arg = context.getArgs()[index];
				log.info("在接口方法【{}】的形式参数列表中找到了参数：{}", methodName, arg);
			} else {
				throw new ZddLogParamenterNotFoundException(
						String.format("在接口方法【%s】的形式参数列表中通过@ZddLogParameter注解没有找到字段【%s】", methodName, parameterName));
			}

			if (parameterNameParts.size() == 1) {
				// 这就是最后一层，处理最终的结果
				processLastLevelFieldValue(part, arg, valueList, methodName, parameterName);

			} else {
				// 递归继续查找下面的层级
				recurseFindFieldValue(1, arg, parameterNameParts, valueList, methodName, parameterName);
			}

			// 每个field对应的值，可能会对应一个列表，表示要查询多个
			fieldValuesMap.put(fieldName, valueList);
		}
	}

	/**
	 * 递归查找请求参数中需要用来作为Entity查询条件的字段和值
	 *
	 * @param level              当前配置的@ZddLogParameter.parameters()方法值的层级。
	 * @param arg                需要在这个对象的属性中查找指定level需要的字段和值，即这个arg应该是上一层的参数对象
	 * @param parameterNameParts 整个参数调用层级列表，比如”formBean.reqdata.id“ -> ["formBean", "reqdata", "id"]
	 * @param valueList          找到的最终的请求参数值
	 * @param methodName         接口方法名
	 * @param parameterName      需要查找的参数名，比如”formBean.reqdata.id“
	 */
	private void recurseFindFieldValue(int level, Object arg, List<String> parameterNameParts, List<Object> valueList, String methodName,
	                                   String parameterName) {

		String part = parameterNameParts.get(level);
		if (ReflectionUtil.isPrimitiveOrString(arg.getClass())) {
			// 如果当前的arg是基础类型，不存在属性Field列表，无法找到目标field
			throw new ZddLogParamenterNotFoundException(String.format(
					"在接口方法【%s】中通过@ZddLogParameter注解在请求参数【%s】中查找失败，因为要查找的参数无法匹配，当前请求参数是基础类型【%s】，要查找的字段名称为【%s】, 全部字段parameter是【%s】",
					methodName, arg, arg.getClass(), part, parameterName));
		}

		// 判断arg是否是数组类型
		if (isArrayOrCollection(arg)) {
			List<Object> list = parseAsList(arg);
			// 在list中循环，每个都要递归查找
			for (Object o : list) {
				recurseFindFieldValue(level + 1, o, parameterNameParts, valueList, methodName, parameterName);
			}
		} else {
			// 请求参数中不存在这个字段，抛异常
			if (!ReflectionUtil.hasField(arg.getClass(), part)) {
				throw new ZddLogParamenterNotFoundException(
						String.format("在接口方法【%s】中通过@ZddLogParameter注解在请求参数【%s】类中查找参数失败，因为在请求参数对象中没有【%s】这个字段，全部字段parameter是【%s】",
								methodName,
								arg.getClass(), part, parameterName));
			}

			// 获取这个请求参数
			arg = ReflectionUtil.getValue(arg, part);

			if (level == parameterNameParts.size() - 1) {
				// 已经到最后一层了，处理最终的结果
				processLastLevelFieldValue(part, arg, valueList, methodName, parameterName);
			} else {
				// 还有下一层，继续递归
				recurseFindFieldValue(level + 1, arg, parameterNameParts, valueList, methodName, parameterName);
			}
		}
	}

	private void checkLastLevelParameterField(String part, Parameter param, String methodName, String parameterName) {
		/*Class<?> paramType = param.getType();
		// 判断是否是这个格式userids[i]，则表示为逗号分隔的int型id列表，如果是userids[]则表示为逗号分隔的String型字符串列表
		int multiPartType = 0; // 表示默认String
		// 是否是多参数型的
		boolean multiValues = isMultiPart(part);
		if (multiValues) {
			multiPartType = getMultiPartType(part);
		}
		if (multiValues && paramType.isAssignableFrom(String.class)) {
			// 正常

		} else if (paramType.isArray() || paramType.isAssignableFrom(Collection.class)) {

			// 是数组请求参数，只能是基本类型的才能作为查询参数
			List<Object> list = parseAsList(arg);
			if (ReflectionUtil.isPrimitiveOrString(list.get(0).getClass())) {
			} else {
				throw new ZddLogParamenterNotFoundException(
						String.format("在接口方法【%s】中通过@ZddLogParameter注解查找参数失败，因为在请求参数对象是数组类型【%s】，且不是基本类型的数组，不能作为查询参数，要查找的字段名称为【%s】",
								methodName, arg.getClass(), parameterName));
			}
		} else if (ReflectionUtil.isPrimitiveOrString(arg.getClass())) {
			// 既不是逗号分隔的字符串，也不是数组类型参数，那就判断是否是基本类型，如果是那就可以作为查询参数，如果不是就抛异常
			valueList.add(arg);
		} else {
			throw new ZddLogParamenterNotFoundException(
					String.format("在接口方法【%s】中通过@ZddLogParameter注解查找参数失败，因为在请求参数对象不是基本类型【%s】，不能作为查询参数，要查找的字段名称为【%s】", methodName,
							arg.getClass(), parameterName));
		}*/
	}

	private void processLastLevelFieldValue(String part, Object arg, List<Object> valueList, String methodName, String parameterName) {

		// 判断是否是这个格式userids[i]，则表示为逗号分隔的int型id列表，如果是userids[]则表示为逗号分隔的String型字符串列表
		int multiPartType = 0; // 表示默认String
		// 是否是多参数型的
		boolean multiValues = isMultiPart(part);
		if (multiValues) {
			multiPartType = getMultiPartType(part);
		}
		if (multiValues && arg instanceof String) {
			// 逗号分隔的字符串类型参数
			if (multiPartType == 1) {
				// int型逗号分隔
				valueList.addAll(StringUtil.splitToInt((String) arg, ","));
			} else {
				valueList.addAll(StringUtil.splitToStr((String) arg, ","));
			}

		} else if (isArrayOrCollection(arg)) {
			// 是数组请求参数，只能是基本类型的才能作为查询参数
			List<Object> list = parseAsList(arg);
			if (ReflectionUtil.isPrimitiveOrString(list.get(0).getClass())) {
				valueList.addAll(list);
			} else {
				throw new ZddLogParamenterNotFoundException(
						String.format("在接口方法【%s】中通过@ZddLogParameter注解查找参数失败，因为在请求参数对象是数组类型【%s】，且不是基本类型的数组，不能作为查询参数，要查找的字段名称为【%s】",
								methodName, arg.getClass(), parameterName));
			}
		} else if (ReflectionUtil.isPrimitiveOrString(arg.getClass())) {
			// 既不是逗号分隔的字符串，也不是数组类型参数，那就判断是否是基本类型，如果是那就可以作为查询参数，如果不是就抛异常
			valueList.add(arg);
		} else {
			throw new ZddLogParamenterNotFoundException(
					String.format("在接口方法【%s】中通过@ZddLogParameter注解查找参数失败，因为在请求参数对象不是基本类型【%s】，不能作为查询参数，要查找的字段名称为【%s】", methodName,
							arg.getClass(), parameterName));
		}
	}

	/**
	 * 判断是否是这个格式userids[i]，则表示为逗号分隔的int型id列表，如果是userids[]则表示为逗号分隔的String型字符串列表
	 *
	 * @param part
	 * @return
	 */
	private boolean isMultiPart(String part) {
		if (part.endsWith("]")) {
			return true;
		}
		return false;
	}

	private String pruneMultiPart(String part) {
		int endIndex = part.indexOf("[");
		if (endIndex < 0) return part;
		return part.substring(0, endIndex);
	}

	/**
	 * userids[i]则表示为逗号分隔的int型id列表，返回1. 如果是userids[]则表示为逗号分隔的String型字符串列表，返回0
	 *
	 * @param part
	 * @return 0：默认字符串，1：int型
	 */
	private int getMultiPartType(String part) {
		int startIndex = part.indexOf("[");
		String sub = part.substring(startIndex);
		return sub.contains("i") ? 1 : 0;
	}

	private boolean isArrayOrCollection(Object arg) {
		return arg != null && (arg instanceof Collection || arg.getClass().isArray());
	}

	private List<Object> parseAsList(Object arg) {
		if (arg instanceof Collection) {
			return Lists.newArrayList((Collection) arg);
		} else if (arg.getClass().isArray()) {
			return Arrays.asList(arg);
		}
		return Lists.newArrayList(arg);
	}
}
